import JNetParam from "./entity/JNetParam";
import NDataUtil from "./util/NDataUtil";
import Singleton from "../util/Singleton";
import NGameMessage from "../util/NGameMessage";
import SystemBase from "../system/SystemBase";
import { NActionServer } from "../action/core/NActionServer";

//WebSocket 连接的 JNetGame
export default abstract class JNSocket extends SystemBase{

    socket:WebSocket;

    id:number = 0;

    events:{ [key: string]: {
        func,
        target,
        className,
        root,
    }[] } = {};

    //是否连接成功
    isConnect:boolean = false;

    //消息返回回调
    callback:Map<number,{type:string,fun:Function}> = new Map;
    
    //延迟 (10s)
    timeout:number = 10000;

    public async onInit(){
        await this.onConnect();
    }

    private async onConnect(resolve?:Function){
        if(resolve){
            if(this.isConnect) return;
            this.isConnect = true;
            
            this.socket = new WebSocket(`${await this.url()}`);
            this.socket.binaryType = 'arraybuffer';
            this.socket.onopen = () => {
                console.log(`NGame WebSocket 连接成功`);
                resolve();
            }
            this.socket.onmessage = this.onMessage.bind(this);
            this.socket.onclose = () => {
                console.log(`NGame WebSocket 断开连接`);
                this.isConnect = false;
                setTimeout(async () => {this.onConnect(resolve)},1000)
            }
            return;
        }else{
            await new Promise(resolve => {
                this.onConnect(resolve);
            })
            return;
        }
    }

    //接收到消息
    private onMessage(message:MessageEvent){
        this.emit(NDataUtil.Parse(message.data));
    }

    private emit(data:JNetParam){
        
        //处理方法0可能是返回消息
        if(data.hId == 0){

            //回调方法
            let callback = this.callback.get(data.id);
            if(callback){
                this.callback.get(data.id).fun(NGameMessage.getIns().Byte4ToData(callback.type,data.byte));
            }

        }else{

            if(!this.events[`${data.hId}`]) this.events[`${data.hId}`] = [];
            //广播
            this.events[`${data.hId}`].forEach(item => {
                if(item.className){
                    if(item.root){
                    }else{
                        item.func.bind(item.target)(NGameMessage.getIns().Byte4ToData(item.className,data.byte));
                    }
                }else{
                    item.func.bind(item.target)();
                }
            })

        }
    }

    //外部监听消息
    public on(hId:number, func: Function,target?:any, className?:string, root?:string){
        // this.event.on(`${hId}`,func,target);
        if(!this.events[`${hId}`]) this.events[`${hId}`] = [];
        this.events[`${hId}`].push({
            func,
            target,
            className,
            root,
        })
    }

    //删除外部监听
    public off(hId:number, func: Function,target?:any, className?:string, root?:string){
        if(!this.events[`${hId}`]) this.events[`${hId}`] = [];
        for (let index = 0; index < this.events[`${hId}`].length; index++) {
            const item = this.events[`${hId}`][index];
            if(item.func == func && item.target == target && item.className == className && item.root == root){
                this.events[`${hId}`].splice(index,1);
                index--;
            }
        }
    }

    //发送消息
    Send(hId:number,data?:any,type?:string){
        let send = NDataUtil.Encrypt(JNetParam.Build(this.id++,hId).setData(data),type);
        console.log("send",send);
        this.socket.send(send);
    }

    //发送消息并且等待返回 (一般用来接受可能返回的消息)
    SendBack(hId:number,back:{type:string,fun:Function},data?:any,type?:string){
        let id = this.id++;
        let send = NDataUtil.Encrypt(JNetParam.Build(id,hId).setData(data),type);
        console.log("sendback",send);
        this.socket.send(send);
        //创建返回监听
        this.callback.set(id,back);
        //如果长时间没有返回则移除
        setTimeout(() => {
            if(this.callback.get(id)){
                //移除
                this.callback.set(id,null);
            }
        },this.timeout)
    }

    public abstract url():Promise<string>;

}

